package com.knife.permission.builder.impl;


import com.knife.permission.builder.DataPermissionBuilder;
import com.knife.permission.entity.DataPermission;
import com.knife.permission.entity.OrgInfo;
import com.knife.permission.entity.OrderColumn;
import com.knife.permission.entity.UserInfo;
import com.knife.permission.plugins.function.OrgInfoFunction;
import com.knife.permission.plugins.function.UserInfoFunction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.knife.common.constants.BaseConstants.SqlSymbol.*;


/**
 * 自定义默认的权限对象构造器
 *
 * @author tianhao.luo@hand-china.com
 */
public class DefaultDataPermissionBuilder implements DataPermissionBuilder<OrgInfo, UserInfo> {
    /**
     * 临时表名
     */
    public static final String BASE_ORG_TABLE_ALIAS = "PMS_ORG_DEFAULT_ALIAS";
    public static final String BASE_PERSON_TABLE_ALIAS = "PMS_PERSON_DEFAULT_ALIAS";

    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultDataPermissionBuilder.class);


    /**
     * 返回如下的sql：left join hr_org baseTableAlias on baseTableAlias.onColumn=headerTableAlias.primaryKeyColumn
     *
     * @param headerTableAlias 单据表名
     * @param primaryKeyColumn 单据表关联字段名
     * @param baseTableName    组织基础表名
     * @param baseTableAlias   组织基础表别名
     * @param onColumn         单据表关联列名
     * @return sql
     */
    private String buildTableJoin(String headerTableAlias, String primaryKeyColumn, String baseTableName, String baseTableAlias, String onColumn) {
        Assert.isTrue(!StringUtils.isEmpty(headerTableAlias), "表头别名不能为空！");
        Assert.isTrue(!StringUtils.isEmpty(primaryKeyColumn), "负责人组织id列名不能为空！");
        StringBuilder sb = new StringBuilder();
        // 例如下面这段sql
        // from orderTable headerTableAlias
        // left join hr_org baseTableAlias on baseTableAlias.onColumn=headerTableAlias.primaryKeyColumn

        // left join hr_org baseTableAlias
        sb.append(LEFT_JOIN).append(SPACE).append(baseTableName).append(SPACE).append(baseTableAlias).append(SPACE)
                // on baseTableAlias.onColumn
                .append(ON).append(SPACE).append(baseTableAlias).append(POINT).append(onColumn)
                // =
                .append(EQUAL)
                // headerTableAlias.primaryKeyColumn
                .append(headerTableAlias).append(POINT).append(primaryKeyColumn)
                .append(WRAP);
        String leftJoinSql = sb.toString();
        if (!StringUtils.isEmpty(leftJoinSql)) {
            return leftJoinSql;
        } else {
            return null;
        }
    }

    /**
     * 返回如下的sql：(baseTableAlias.baseColumn like 'levelPath%') 或者 (baseTableAlias.baseColumn in (employeeId))
     *
     * @param baseTableAlias  基础组织表别名
     * @param baseColumn      过滤的列名，可能是levelPath也可能是unitId
     * @param isLike          是否模糊匹配（模糊匹配使用levelPath）
     * @param orgInfoList   组织集合
     * @param orgInfoFunction 组织levelPath获取操作
     * @return sql
     */
    private String buildOrgWhereSql(String baseTableAlias,
                                    String baseColumn,
                                    Boolean isLike,
                                    List<OrgInfo> orgInfoList,
                                    OrgInfoFunction<OrgInfo> orgInfoFunction) {
        if (CollectionUtils.isEmpty(orgInfoList)) {
            return null;
        }
        Assert.isTrue(!StringUtils.isEmpty(baseTableAlias), "基础表别名不能为空！");
        Assert.isTrue(!StringUtils.isEmpty(baseColumn), "组织id或层级关系列名不能为空！");
        StringBuilder sb = new StringBuilder();
        // 例如下面这段sql
        // 1. 模糊查询
        // where baseTableAlias.baseColumn like 'levelPath%' or baseTableAlias.baseColumn like 'levelPath%'
        // 2。 全值匹配
        // where baseTableAlias.baseColumn in (employeeId)
        if (isLike) {
            // 构造出：'levelPath1%' or 'levelPath2%'
            List<OrgInfo> currOrgInfoLevelPathList = orgInfoList.stream().filter(item -> orgInfoFunction.getLevelPath(item) != null).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(currOrgInfoLevelPathList)) {
                String levelPathCondition = currOrgInfoLevelPathList.stream().map(item -> {
                    String levelPath = orgInfoFunction.getLevelPath(item);
                    // 形如：baseTableAlias.baseColumn like 'levelPath%'
                    return baseTableAlias + POINT + baseColumn + SPACE + LIKE + SPACE + SINGLE_QUOTATION + levelPath + PERCENT_SIGN + SINGLE_QUOTATION;
                }).collect(Collectors.joining(SPACE + OR + SPACE));
                // 构造出： baseTableAlias.baseColumn like 'levelPath%'
                sb.append(LEFT_BRACE).append(levelPathCondition).append(RIGHT_BRACE);
            }
        } else {
            // 构造出： orgId1,orgId2
            String orgIds = orgInfoList.stream().map(orgInfoFunction::getUnitId).map(Objects::toString).collect(Collectors.joining(COMMA));
            // 构造出：in ( orgIds )
            sb.append(LEFT_BRACE).append(baseTableAlias).append(POINT).append(baseColumn).append(SPACE)
                    .append(IN).append(SPACE).append(LEFT_BRACE).append(orgIds).append(RIGHT_BRACE).append(RIGHT_BRACE);
        }
        String whereSql = sb.toString();
        if (!StringUtils.isEmpty(whereSql)) {
            return whereSql;
        } else {
            return null;
        }
    }


    /**
     * 返回如下的sql：(baseTableAlias.baseColumn=employeeId)
     *
     * @param baseTableAlias 基础表别名
     * @param baseColumn     关联列id
     * @param employeeId     员工id
     * @param isLong         单据关联字段是否long类型
     * @return sql
     */
    private String buildPersonWhereSql(String baseTableAlias, String baseColumn, Long employeeId, boolean isLong) {
        if (employeeId == null || employeeId == -1L) {
            return null;
        }
        Assert.isTrue(!StringUtils.isEmpty(baseTableAlias), "基础表头别名不能为空！");
        Assert.isTrue(!StringUtils.isEmpty(baseColumn), "基础表id列名不能为空！");
        StringBuilder sb = new StringBuilder();
        if (isLong) {
            // where (baseTableAlias.baseColumn=employeeId)
            sb.append(LEFT_BRACE).append(baseTableAlias).append(POINT).append(baseColumn).append(EQUAL).append(employeeId).append(RIGHT_BRACE);
        } else {
            // where (baseTableAlias.baseColumn='employeeId')
            sb.append(LEFT_BRACE).append(baseTableAlias).append(POINT).append(baseColumn).append(EQUAL).append(SINGLE_QUOTATION).append(employeeId).append(SINGLE_QUOTATION).append(RIGHT_BRACE);
        }
        String whereSql = sb.toString();
        if (!StringUtils.isEmpty(whereSql)) {
            return whereSql;
        } else {
            return null;
        }
    }


    /**
     * 返回如下的sql：(baseTableAlias.baseColumn=employeeId)
     *
     * @param baseTableAlias   基础表别名
     * @param baseColumn       关联列id
     * @param userInfo      员工code
     * @param userInfoFunction 获取用户信息操作
     * @return sql
     */
    private String buildPersonWhereSqlOfEmployeeCode(String baseTableAlias,
                                                     String baseColumn,
                                                     UserInfo userInfo,
                                                     UserInfoFunction<UserInfo> userInfoFunction) {
        if (userInfo == null || userInfo.getEmployeeCode() == null) {
            return null;
        }
        Assert.isTrue(!StringUtils.isEmpty(baseTableAlias), "基础表头别名不能为空！");
        Assert.isTrue(!StringUtils.isEmpty(baseColumn), "基础表id列名不能为空！");
        StringBuilder sb = new StringBuilder();
        // where (baseTableAlias.baseColumn=employeeId)
        sb.append(LEFT_BRACE)
                .append(baseTableAlias)
                .append(POINT)
                .append(baseColumn)
                .append(EQUAL)
                .append(SINGLE_QUOTATION)
                .append(userInfoFunction.getEmployeeCode(userInfo))
                .append(SINGLE_QUOTATION)
                .append(RIGHT_BRACE);
        String whereSql = sb.toString();
        if (!StringUtils.isEmpty(whereSql)) {
            return whereSql;
        } else {
            return null;
        }
    }


    /**
     * 返回如下的sql：(baseTableAlias.baseColumn=employeeId)
     *
     * @param baseTableAlias   基础表别名
     * @param baseColumn       关联列id
     * @param userInfo      员工code
     * @param userInfoFunction 获取用户信息操作
     * @return sql
     */
    private String buildPersonWhereSqlOfUserId(String baseTableAlias,
                                               String baseColumn,
                                               UserInfo userInfo,
                                               UserInfoFunction<UserInfo> userInfoFunction) {
        if (userInfo == null || userInfo.getUserId() == null) {
            return null;
        }
        Assert.isTrue(!StringUtils.isEmpty(baseTableAlias), "基础表头别名不能为空！");
        Assert.isTrue(!StringUtils.isEmpty(baseColumn), "基础表id列名不能为空！");
        StringBuilder sb = new StringBuilder();
        // where (baseTableAlias.baseColumn=employeeId)
        sb.append(LEFT_BRACE)
                .append(baseTableAlias)
                .append(POINT)
                .append(baseColumn)
                .append(EQUAL)
                .append(userInfoFunction.getUserId(userInfo))
                .append(RIGHT_BRACE);
        return sb.toString();
    }


    /**
     * 通过组织权限维度，构造权限
     *
     * @param orgDataPermissionList 组织权限维度
     * @param leftJoinSqlList       存放左连接
     * @param whereSqlList          存放条件集合
     * @param orgUnitList           当前组织
     * @param orgInfoFunction       组织levelPath获取操作
     */
    private void creatOrgDataPermissionList(List<OrderColumn> orgDataPermissionList,
                                            List<String> leftJoinSqlList,
                                            List<String> whereSqlList,
                                            List<OrgInfo> orgUnitList,
                                            OrgInfoFunction<OrgInfo> orgInfoFunction) {
        // 组织权限
        if (orgDataPermissionList != null && orgDataPermissionList.size() != 0) {
            for (int i = 0; i < orgDataPermissionList.size(); i++) {
                OrderColumn orgPermissionOrderDto = orgDataPermissionList.get(i);
                String baseTableAlias = BASE_ORG_TABLE_ALIAS + i;
                // 返回如下sql：left join hr_org baseTableAlias on baseTableAlias.onColumn=headerTableAlias.primaryKeyColumn
                String orgLeftJoinSql = buildTableJoin(
                        orgPermissionOrderDto.getOrderTableAlias(),
                        orgPermissionOrderDto.getOrderTableColumn(),
                        orgPermissionOrderDto.getBaseTableName(),
                        baseTableAlias,
                        orgPermissionOrderDto.getBaseTableJoinOnColumn());
                // 返回如下sql：baseTableAlias.baseColumn like 'levelPath%' 或者 baseTableAlias.baseColumn in (orgId)
                String whereSql = buildOrgWhereSql(baseTableAlias,
                        orgPermissionOrderDto.getBaseTableFilterColumn(),
                        orgPermissionOrderDto.getBaseTableLikeFlag() == 1,
                        orgUnitList,
                        orgInfoFunction);
                leftJoinSqlList.add(orgLeftJoinSql);
                if (whereSql != null) {
                    whereSqlList.add(whereSql);
                }
            }
        }

    }


    /**
     * 通过用户权限维度，构造权限
     *
     * @param personDataPermissionList 用户权限维度
     * @param leftJoinSqlList          存放左连接sql
     * @param whereSqlList             条件sql
     * @param userInfo              用户信息
     * @param userInfoFunction         用户信息获取操作
     */
    private void createPersonDataPermissionList(List<OrderColumn> personDataPermissionList,
                                                List<String> leftJoinSqlList,
                                                List<String> whereSqlList,
                                                UserInfo userInfo,
                                                UserInfoFunction<UserInfo> userInfoFunction) {
        if (personDataPermissionList != null && personDataPermissionList.size() != 0) {
            for (int i = 0; i < personDataPermissionList.size(); i++) {
                OrderColumn personOrderColumn = personDataPermissionList.get(i);
                String baseTableAlias = BASE_PERSON_TABLE_ALIAS + i;
                String personLeftJoinSql = buildTableJoin(
                        personOrderColumn.getOrderTableAlias(),
                        personOrderColumn.getOrderTableColumn(),
                        personOrderColumn.getBaseTableName(),
                        baseTableAlias,
                        personOrderColumn.getBaseTableJoinOnColumn());
                leftJoinSqlList.add(personLeftJoinSql);
                if ("user_id".equalsIgnoreCase(personOrderColumn.getBaseTableFilterColumn())) {
                    String whereSql = buildPersonWhereSqlOfUserId(baseTableAlias,
                            personOrderColumn.getBaseTableFilterColumn(),
                            userInfo,
                            userInfoFunction);
                    if (whereSql != null) {
                        whereSqlList.add(whereSql);
                    }
                } else if ("employee_code".equalsIgnoreCase(personOrderColumn.getBaseTableFilterColumn())) {
                    String whereSql = buildPersonWhereSqlOfEmployeeCode(baseTableAlias,
                            personOrderColumn.getBaseTableFilterColumn(),
                            userInfo,
                            userInfoFunction);
                    if (whereSql != null) {
                        whereSqlList.add(whereSql);
                    }
                }
            }
        }
    }

    @Override
    public DataPermission buildMultipleTableJoinAndWhereSql(List<OrderColumn> dataPermissionOrderList,
                                                            List<OrgInfo> orgUnitList,
                                                            OrgInfoFunction<OrgInfo> orgInfoFunction,
                                                            UserInfo userInfo,
                                                            UserInfoFunction<UserInfo> userInfoFunction) {
        Map<Integer, List<OrderColumn>> map = dataPermissionOrderList.stream().collect(Collectors.groupingBy(OrderColumn::getBaseTableDimensionFlag));
        List<OrderColumn> orgDataPermissionList = map.get(0);
        List<OrderColumn> personDataPermissionList = map.get(1);

        List<String> leftJoinSqlList = new ArrayList<>();
        List<String> whereSqlList = new ArrayList<>();
        // 组织权限
        creatOrgDataPermissionList(orgDataPermissionList, leftJoinSqlList, whereSqlList, orgUnitList, orgInfoFunction);
        // 人员权限
        createPersonDataPermissionList(personDataPermissionList, leftJoinSqlList, whereSqlList, userInfo, userInfoFunction);
        return new DataPermission(LEFT_BRACE + String.join(SPACE + OR + SPACE, whereSqlList) + RIGHT_BRACE,
                String.join(SPACE, leftJoinSqlList));
    }
}
